home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / lock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  14.4 KB  |  640 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.4 kit.
  7.  * 
  8.  * Set Lock
  9.  * 
  10.  * Lock file support for CVS.
  11.  */
  12.  
  13. #include "cvs.h"
  14.  
  15. static int readers_exist PROTO((char *repository));
  16. static int set_lock PROTO((char *repository, int will_wait));
  17. static void clear_lock PROTO((void));
  18. static void set_lockers_name PROTO((struct stat *statp));
  19. static int set_writelock_proc PROTO((Node * p, void *closure));
  20. static int unlock_proc PROTO((Node * p, void *closure));
  21. static int write_lock PROTO((char *repository));
  22. static void lock_simple_remove PROTO((char *repository));
  23. static void lock_wait PROTO((char *repository));
  24. static int Check_Owner PROTO((char *lockdir));
  25.  
  26. static char lockers_name[20];
  27. static char *repository;
  28. static char readlock[PATH_MAX], writelock[PATH_MAX], masterlock[PATH_MAX];
  29. static int cleanup_lckdir;
  30. static List *locklist;
  31.  
  32. #define L_OK        0        /* success */
  33. #define L_ERROR        1        /* error condition */
  34. #define L_LOCKED    2        /* lock owned by someone else */
  35.  
  36. /*
  37.  * Clean up all outstanding locks
  38.  */
  39. void
  40. Lock_Cleanup ()
  41. {
  42.     /* clean up simple locks (if any) */
  43.     if (repository != NULL)
  44.     {
  45.     lock_simple_remove (repository);
  46.     repository = (char *) NULL;
  47.     }
  48.  
  49.     /* clean up multiple locks (if any) */
  50.     if (locklist != (List *) NULL)
  51.     {
  52.     (void) walklist (locklist, unlock_proc, NULL);
  53.     locklist = (List *) NULL;
  54.     }
  55. }
  56.  
  57. /*
  58.  * walklist proc for removing a list of locks
  59.  */
  60. static int
  61. unlock_proc (p, closure)
  62.     Node *p;
  63.     void *closure;
  64. {
  65.     lock_simple_remove (p->key);
  66.     return (0);
  67. }
  68.  
  69. /*
  70.  * Remove the lock files (without complaining if they are not there),
  71.  */
  72. static void
  73. lock_simple_remove (repository)
  74.     char *repository;
  75. {
  76.     char tmp[PATH_MAX];
  77.  
  78.     if (readlock[0] != '\0')
  79.     {
  80.     (void) sprintf (tmp, "%s/%s", repository, readlock);
  81.     if (unlink (tmp) < 0 && ! existence_error (errno))
  82.         error (0, errno, "failed to remove lock %s", tmp);
  83.     }
  84.  
  85.     if (writelock[0] != '\0')
  86.     {
  87.     (void) sprintf (tmp, "%s/%s", repository, writelock);
  88.     if (unlink (tmp) < 0 && ! existence_error (errno))
  89.         error (0, errno, "failed to remove lock %s", tmp);
  90.     }
  91.  
  92.     /*
  93.      * Only remove the lock directory if it is ours, note that this does
  94.      * lead to the limitation that one user ID should not be committing
  95.      * files into the same Repository directory at the same time. Oh well.
  96.      */
  97.     if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)) 
  98.     {
  99.         (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
  100.             if (Check_Owner(tmp))
  101.         {
  102. #ifdef AFSCVS
  103.         char rmuidlock[PATH_MAX];
  104.         sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() );
  105.         system(rmuidlock);
  106. #endif
  107.         (void) rmdir (tmp);
  108.         }
  109.     }
  110.     cleanup_lckdir = 0;
  111. }
  112.  
  113. /*
  114.  * Check the owner of a lock.  Returns 1 if we own it, 0 otherwise.
  115.  */
  116. static int
  117. Check_Owner(lockdir)
  118.      char *lockdir;
  119. {
  120.   struct stat sb;
  121.  
  122. #ifdef AFSCVS
  123.   /* In the Andrew File System (AFS), user ids from stat don't match
  124.      those from geteuid().  The AFSCVS code can deal with either AFS or
  125.      non-AFS repositories; the non-AFSCVS code is faster.  */
  126.   char uidlock[PATH_MAX];
  127.  
  128.   /* Check if the uidlock is in the lock directory */
  129.   sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() );
  130.   if( stat(uidlock, &sb) != -1)
  131.     return 1;   /* The file exists, therefore we own the lock */
  132.   else
  133.     return 0;     /* The file didn't exist or some other error.
  134.          * Assume that we don't own it.
  135.          */
  136. #else
  137.   if (stat (lockdir, &sb) != -1 && sb.st_uid == geteuid ())
  138.     return 1;
  139.   else
  140.     return 0;
  141. #endif
  142. }  /* end Check_Owner() */
  143.  
  144.  
  145. /*
  146.  * Create a lock file for readers
  147.  */
  148. int
  149. Reader_Lock (xrepository)
  150.     char *xrepository;
  151. {
  152.     int err = 0;
  153.     FILE *fp;
  154.     char tmp[PATH_MAX];
  155.  
  156.     if (noexec)
  157.     return (0);
  158.  
  159.     /* we only do one directory at a time for read locks! */
  160.     if (repository != NULL)
  161.     {
  162.     error (0, 0, "Reader_Lock called while read locks set - Help!");
  163.     return (1);
  164.     }
  165.  
  166.     if (readlock[0] == '\0')
  167.       (void) sprintf (readlock, 
  168. #ifdef HAVE_LONG_FILE_NAMES
  169.         "%s.%s.%ld", CVSRFL, hostname,
  170. #else
  171.         "%s.%ld", CVSRFL,
  172. #endif
  173.         (long) getpid ());
  174.  
  175.     /* remember what we're locking (for lock_cleanup) */
  176.     repository = xrepository;
  177.  
  178.     /* get the lock dir for our own */
  179.     if (set_lock (xrepository, 1) != L_OK)
  180.     {
  181.     error (0, 0, "failed to obtain dir lock in repository `%s'",
  182.            xrepository);
  183.     readlock[0] = '\0';
  184.     return (1);
  185.     }
  186.  
  187.     /* write a read-lock */
  188.     (void) sprintf (tmp, "%s/%s", xrepository, readlock);
  189.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  190.     {
  191.     error (0, errno, "cannot create read lock in repository `%s'",
  192.            xrepository);
  193.     readlock[0] = '\0';
  194.     err = 1;
  195.     }
  196.  
  197.     /* free the lock dir */
  198.     clear_lock();
  199.  
  200.     return (err);
  201. }
  202.  
  203. /*
  204.  * Lock a list of directories for writing
  205.  */
  206. static char *lock_error_repos;
  207. static int lock_error;
  208. int
  209. Writer_Lock (list)
  210.     List *list;
  211. {
  212.     if (noexec)
  213.     return (0);
  214.  
  215.     /* We only know how to do one list at a time */
  216.     if (locklist != (List *) NULL)
  217.     {
  218.     error (0, 0, "Writer_Lock called while write locks set - Help!");
  219.     return (1);
  220.     }
  221.  
  222.     for (;;)
  223.     {
  224.     /* try to lock everything on the list */
  225.     lock_error = L_OK;        /* init for set_writelock_proc */
  226.     lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
  227.     locklist = list;        /* init for Lock_Cleanup */
  228.     (void) strcpy (lockers_name, "unknown");
  229.  
  230.     (void) walklist (list, set_writelock_proc, NULL);
  231.  
  232.     switch (lock_error)
  233.     {
  234.         case L_ERROR:        /* Real Error */
  235.         Lock_Cleanup ();    /* clean up any locks we set */
  236.         error (0, 0, "lock failed - giving up");
  237.         return (1);
  238.  
  239.         case L_LOCKED:        /* Someone already had a lock */
  240.         Lock_Cleanup ();    /* clean up any locks we set */
  241.         lock_wait (lock_error_repos); /* sleep a while and try again */
  242.         continue;
  243.  
  244.         case L_OK:            /* we got the locks set */
  245.         return (0);
  246.  
  247.         default:
  248.         error (0, 0, "unknown lock status %d in Writer_Lock",
  249.                lock_error);
  250.         return (1);
  251.     }
  252.     }
  253. }
  254.  
  255. /*
  256.  * walklist proc for setting write locks
  257.  */
  258. static int
  259. set_writelock_proc (p, closure)
  260.     Node *p;
  261.     void *closure;
  262. {
  263.     /* if some lock was not OK, just skip this one */
  264.     if (lock_error != L_OK)
  265.     return (0);
  266.  
  267.     /* apply the write lock */
  268.     lock_error_repos = p->key;
  269.     lock_error = write_lock (p->key);
  270.     return (0);
  271. }
  272.  
  273. /*
  274.  * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
  275.  * lock held by someone else or L_ERROR if an error occurred
  276.  */
  277. static int
  278. write_lock (repository)
  279.     char *repository;
  280. {
  281.     int status;
  282.     FILE *fp;
  283.     char tmp[PATH_MAX];
  284.  
  285.     if (writelock[0] == '\0')
  286.     (void) sprintf (writelock,
  287. #ifdef HAVE_LONG_FILE_NAMES
  288.         "%s.%s.%ld", CVSWFL, hostname,
  289. #else
  290.         "%s.%ld", CVSWFL,
  291. #endif
  292.     (long) getpid());
  293.  
  294.     /* make sure the lock dir is ours (not necessarily unique to us!) */
  295.     status = set_lock (repository, 0);
  296.     if (status == L_OK)
  297.     {
  298.     /* we now own a writer - make sure there are no readers */
  299.     if (readers_exist (repository))
  300.     {
  301.         /* clean up the lock dir if we created it */
  302.         if (status == L_OK)
  303.         {
  304.         clear_lock();
  305.         }
  306.  
  307.         /* indicate we failed due to read locks instead of error */
  308.         return (L_LOCKED);
  309.     }
  310.  
  311.     /* write the write-lock file */
  312.     (void) sprintf (tmp, "%s/%s", repository, writelock);
  313.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  314.     {
  315.         int xerrno = errno;
  316.  
  317.         if (unlink (tmp) < 0 && ! existence_error (errno))
  318.         error (0, errno, "failed to remove lock %s", tmp);
  319.  
  320.         /* free the lock dir if we created it */
  321.         if (status == L_OK)
  322.         {
  323.         clear_lock();
  324.         }
  325.  
  326.         /* return the error */
  327.         error (0, xerrno, "cannot create write lock in repository `%s'",
  328.            repository);
  329.         return (L_ERROR);
  330.     }
  331.     return (L_OK);
  332.     }
  333.     else
  334.     return (status);
  335. }
  336.  
  337. /*
  338.  * readers_exist() returns 0 if there are no reader lock files remaining in
  339.  * the repository; else 1 is returned, to indicate that the caller should
  340.  * sleep a while and try again.
  341.  */
  342. static int
  343. readers_exist (repository)
  344.     char *repository;
  345. {
  346.     char *line;
  347.     DIR *dirp;
  348.     struct dirent *dp;
  349.     struct stat sb;
  350.     int ret = 0;
  351.  
  352. #ifdef CVS_FUDGELOCKS
  353. again:
  354. #endif
  355.  
  356.     if ((dirp = opendir (repository)) == NULL)
  357.     error (1, 0, "cannot open directory %s", repository);
  358.  
  359.     errno = 0;
  360.     while ((dp = readdir (dirp)) != NULL)
  361.     {
  362.     if (fnmatch (CVSRFLPAT, dp->d_name, 0) == 0)
  363.     {
  364. #ifdef CVS_FUDGELOCKS
  365.         time_t now;
  366.         (void) time (&now);
  367. #endif
  368.  
  369.         line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5);
  370.         (void) sprintf (line, "%s/%s", repository, dp->d_name);
  371.         if (stat (line, &sb) != -1)
  372.         {
  373. #ifdef CVS_FUDGELOCKS
  374.         /*
  375.          * If the create time of the file is more than CVSLCKAGE 
  376.          * seconds ago, try to clean-up the lock file, and if
  377.          * successful, re-open the directory and try again.
  378.          */
  379.         if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
  380.         {
  381.             (void) closedir (dirp);
  382.             free (line);
  383.             goto again;
  384.         }
  385. #endif
  386.         set_lockers_name (&sb);
  387.         }
  388.         else
  389.         {
  390.         /* If the file doesn't exist, it just means that it disappeared
  391.            between the time we did the readdir and the time we did
  392.            the stat.  */
  393.         if (!existence_error (errno))
  394.             error (0, errno, "cannot stat %s", line);
  395.         }
  396.         errno = 0;
  397.         free (line);
  398.  
  399.         ret = 1;
  400.         break;
  401.     }
  402.     errno = 0;
  403.     }
  404.     if (errno != 0)
  405.     error (0, errno, "error reading directory %s", repository);
  406.  
  407.     closedir (dirp);
  408.     return (ret);
  409. }
  410.  
  411. /*
  412.  * Set the static variable lockers_name appropriately, based on the stat
  413.  * structure passed in.
  414.  */
  415. static void
  416. set_lockers_name (statp)
  417.     struct stat *statp;
  418. {
  419.     struct passwd *pw;
  420.  
  421.     if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
  422.     (struct passwd *) NULL)
  423.     {
  424.     (void) strcpy (lockers_name, pw->pw_name);
  425.     }
  426.     else
  427.     (void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
  428. }
  429.  
  430. /*
  431.  * Persistently tries to make the directory "lckdir",, which serves as a
  432.  * lock. If the create time on the directory is greater than CVSLCKAGE
  433.  * seconds old, just try to remove the directory.
  434.  */
  435. static int
  436. set_lock (repository, will_wait)
  437.     char *repository;
  438.     int will_wait;
  439. {
  440.     struct stat sb;
  441.     mode_t omask;
  442. #ifdef CVS_FUDGELOCKS
  443.     time_t now;
  444. #endif
  445.  
  446.     (void) sprintf (masterlock, "%s/%s", repository, CVSLCK);
  447.  
  448.     /*
  449.      * Note that it is up to the callers of set_lock() to arrange for signal
  450.      * handlers that do the appropriate things, like remove the lock
  451.      * directory before they exit.
  452.      */
  453.     cleanup_lckdir = 0;
  454.     for (;;)
  455.     {
  456.     int status = -1;
  457.     omask = umask (cvsumask);
  458.     SIG_beginCrSect ();
  459.     if (CVS_MKDIR (masterlock, 0777) == 0)
  460.     {
  461. #ifdef AFSCVS
  462.         char uidlock[PATH_MAX];
  463.         FILE *fp;
  464.  
  465.         sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() );
  466.         if ((fp = fopen(uidlock, "w+")) == NULL)
  467.         {
  468.         /* We failed to create the uidlock,
  469.            so rm masterlock and leave */
  470.         rmdir(masterlock);
  471.         SIG_endCrSect ();
  472.         status = L_ERROR;
  473.         goto out;
  474.         }
  475.  
  476.         /* We successfully created the uid lock, so close the file */
  477.         fclose(fp);
  478. #endif
  479.         cleanup_lckdir = 1;
  480.         SIG_endCrSect ();
  481.         status = L_OK;
  482.         goto out;
  483.     }
  484.     SIG_endCrSect ();
  485.       out:
  486.     (void) umask (omask);
  487.     if (status != -1)
  488.         return status;
  489.  
  490.     if (errno != EEXIST)
  491.     {
  492.         error (0, errno,
  493.            "failed to create lock directory in repository `%s'",
  494.            repository);
  495.         return (L_ERROR);
  496.     }
  497.  
  498.     /*
  499.      * stat the dir - if it is non-existent, re-try the loop since
  500.      * someone probably just removed it (thus releasing the lock)
  501.      */
  502.     if (stat (masterlock, &sb) < 0)
  503.     {
  504.         if (existence_error (errno))
  505.         continue;
  506.  
  507.         error (0, errno, "couldn't stat lock directory `%s'", masterlock);
  508.         return (L_ERROR);
  509.     }
  510.  
  511. #ifdef CVS_FUDGELOCKS
  512.     /*
  513.      * If the create time of the directory is more than CVSLCKAGE seconds
  514.      * ago, try to clean-up the lock directory, and if successful, just
  515.      * quietly retry to make it.
  516.      */
  517.     (void) time (&now);
  518.     if (now >= (sb.st_ctime + CVSLCKAGE))
  519.     {
  520. #ifdef AFSCVS
  521.       /* Remove the uidlock first */
  522.       char rmuidlock[PATH_MAX];
  523.       sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
  524.       system(rmuidlock);
  525. #endif
  526.         if (rmdir (masterlock) >= 0)
  527.         continue;
  528.     }
  529. #endif
  530.  
  531.     /* set the lockers name */
  532.     set_lockers_name (&sb);
  533.  
  534.     /* if he wasn't willing to wait, return an error */
  535.     if (!will_wait)
  536.         return (L_LOCKED);
  537.     lock_wait (repository);
  538.     }
  539. }
  540.  
  541. /*
  542.  * Clear master lock.  We don't have to recompute the lock name since
  543.  * clear_lock is never called except after a successful set_lock().
  544.  */
  545. static void
  546. clear_lock()
  547. {
  548. #ifdef AFSCVS
  549.   /* Remove the uidlock first */
  550.   char rmuidlock[PATH_MAX];
  551.   sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
  552.   system(rmuidlock);
  553. #endif
  554.     if (rmdir (masterlock) < 0)
  555.     error (0, errno, "failed to remove lock dir `%s'", masterlock);
  556.     cleanup_lckdir = 0;
  557. }
  558.  
  559. /*
  560.  * Print out a message that the lock is still held, then sleep a while.
  561.  */
  562. static void
  563. lock_wait (repos)
  564.     char *repos;
  565. {
  566.     time_t now;
  567.  
  568.     (void) time (&now);
  569.     error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
  570.        lockers_name, repos);
  571.     (void) sleep (CVSLCKSLEEP);
  572. }
  573.  
  574. static int lock_filesdoneproc PROTO ((int err, char *repository,
  575.                       char *update_dir));
  576. static int fsortcmp PROTO((const Node * p, const Node * q));
  577.  
  578. static List *lock_tree_list;
  579.  
  580. /*
  581.  * Create a list of repositories to lock
  582.  */
  583. /* ARGSUSED */
  584. static int
  585. lock_filesdoneproc (err, repository, update_dir)
  586.     int err;
  587.     char *repository;
  588.     char *update_dir;
  589. {
  590.     Node *p;
  591.  
  592.     p = getnode ();
  593.     p->type = LOCK;
  594.     p->key = xstrdup (repository);
  595.     /* FIXME-KRP: this error condition should not simply be passed by. */
  596.     if (p->key == NULL || addnode (lock_tree_list, p) != 0)
  597.     freenode (p);
  598.     return (err);
  599. }
  600.  
  601. /*
  602.  * compare two lock list nodes (for sort)
  603.  */
  604. static int
  605. fsortcmp (p, q)
  606.     const Node *p;
  607.     const Node *q;
  608. {
  609.     return (strcmp (p->key, q->key));
  610. }
  611.  
  612. void
  613. lock_tree_for_write (argc, argv, local, aflag)
  614.     int argc;
  615.     char **argv;
  616.     int local;
  617.     int aflag;
  618. {
  619.     int err;
  620.     /*
  621.      * Run the recursion processor to find all the dirs to lock and lock all
  622.      * the dirs
  623.      */
  624.     lock_tree_list = getlist ();
  625.     err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
  626.                (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, argc,
  627.                argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0,
  628.                0);
  629.     sortlist (lock_tree_list, fsortcmp);
  630.     if (Writer_Lock (lock_tree_list) != 0)
  631.     error (1, 0, "lock failed - giving up");
  632. }
  633.  
  634. void
  635. lock_tree_cleanup ()
  636. {
  637.     Lock_Cleanup ();
  638.     dellist (&lock_tree_list);
  639. }
  640.